home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / util / edit / jed207.lha / src / jed.lha / command.c < prev    next >
C/C++ Source or Header  |  1993-01-06  |  20KB  |  920 lines

  1.  
  2. /*
  3.  * COMMAND.C
  4.  * (c) 1992-3 J.Harper
  5.  *
  6.  * Wack(ish) style command processor
  7.  *
  8.  * explanation time,
  9.  * Each command string is built from clauses, the different types of
  10.  * clause are,
  11.  *
  12.  * (symbol [arg clauses])   command or variable clause
  13.  * `string'                 string clause (quotes nest!), strings can also
  14.  * {string}            be surrounded by curly brackets (to help ARexx
  15.  *                users :-)
  16.  * number            number (hex, octal or decimal) clause,
  17.  * ~x                ascii value of char x (number clause)
  18.  * ^x                control codes
  19.  * @                clause with no value (a placeholder?)
  20.  *
  21.  * escape sequences supported in strings (and characters) are
  22.  *  \n        newline
  23.  *  \r        carriage return
  24.  *  \f        form feed
  25.  *  \t        horizontal tab
  26.  *  \\        backslash
  27.  *  \`          ignored open quote
  28.  *  \'          ignored close quote
  29.  *  \0377    octal byte
  30.  *  \0xff    hex byte
  31.  *  \255    decimal byte
  32.  *
  33.  * There are two variable types, strings and numbers (32 bit signed ints),
  34.  * actually command functions are treated as variables, this is not important.
  35.  *
  36.  * In a command clause there can be more clauses representing that command's
  37.  * arguments, so an example command string could be,
  38.  *  (settitle (format `%s %ld' `a string' 0x100))
  39.  *
  40.  * If a command string is to be passed as an argument it must be enclosed
  41.  * in quotes to stop it being executed too soon, ie,
  42.  *  (if 1 `(req `foo\nbar\n' `go on')')
  43.  *
  44.  * internal notes:
  45.  * Command functions which return NULL are taken as signifying that the macro
  46.  * currently being evaluated is to be aborted, the Value in the RES field
  47.  * of that command is taken as the result of the whole macro (this is to
  48.  * make a (return) command easy :-)
  49.  */
  50.  
  51. #include "jed.h"
  52. #include "jed_protos.h"
  53.  
  54. Prototype   GSYM *    addgsym        (STRPTR, BYTE, VALUE *);
  55. Prototype   LSYM *    addlsym        (STRPTR, BYTE, VALUE *);
  56. Prototype   BOOL    remsym        (STRPTR);
  57. Local        VALUE *    resolveclause    (STRPTR *, VALUE *);
  58. Local        BOOL    buildargv    (STRPTR *, LONG *, VALUE **);
  59. Local        VALUE *    resolvesym    (STRPTR, LONG, VALUE *, VALUE *);
  60. Local        VALUE *    execsym        (SYM *, LONG, VALUE *, VALUE *);
  61. Local        BOOL    expandargs    (STRPTR, LONG, VALUE *, LONG);
  62. Prototype   VOID    releasevalue    (VALUE *);
  63. Prototype   BOOL    dupvalue    (VALUE *, VALUE *);
  64. Local        VOID    releaseargs    (LONG, VALUE *);
  65. Prototype   BOOL    templatecmd    (LONG, VALUE *, LONG);
  66. Prototype   VALUE *    execstr        (STRPTR, VALUE *, BOOL, LONG, VALUE *);
  67. Prototype   LSYM *    findlsym    (STRPTR);
  68. Prototype   BOOL    findsargs    (LONG *, VALUE **);
  69. Prototype   VOID    rxexecstring    (STRPTR, APTR);
  70. Prototype   BOOL    scriptfile    (STRPTR);
  71. Local        STRPTR    nextclause    (STRPTR);
  72. Prototype   VALUE *    cmd_symboldump    (LONG, VALUE *);
  73. Local        VOID    dumponesym    (SYM *, BPTR);
  74.  
  75. Prototype   const UBYTE NoArgMsg[];
  76. Prototype   const UBYTE NotStringMsg[];
  77. Prototype   const UBYTE NotNumerMsg[];
  78. Prototype   const UBYTE BadArgMsg[];
  79.  
  80. const UBYTE NoArgMsg[] =     "syntax error: no argument specified";
  81. const UBYTE NotStringMsg[] = "syntax error: not string value";
  82. const UBYTE NotNumerMsg[] =  "syntax error: not numerical value";
  83. const UBYTE BadArgMsg[] =    "syntax error: incorrect argument given";
  84.  
  85. Prototype   HASHNODE *SymbolTab[GSYMTABSIZE];
  86. Prototype   CS         CSList[];
  87. Prototype   WORD     CSDepth;
  88.  
  89. HASHNODE *SymbolTab[GSYMTABSIZE];
  90. CS     CSList[MAX_CS_RECURSE];
  91. WORD     CSDepth;
  92.  
  93. /*
  94.  * value (any strings) will be allocated or whatever.
  95.  */
  96. GSYM *
  97. addgsym(STRPTR name, BYTE symType, VALUE *value)
  98. {
  99.     GSYM *newsym;
  100. /*
  101.  *  if(newsym = (GSYM *)findhash(SymbolTab, name))
  102.  *  {
  103.  *    releasevalue(&newsym->gs_Sym.sym_Value);
  104.  *    newsym->gs_Sym.sym_Type = symType;
  105.  *    dupvalue(&newsym->gs_Sym.sym_Value, value);
  106.  *    return(newsym);
  107.  *  }
  108.  *  else
  109.  */
  110.     if(newsym = AllocVec(sizeof(GSYM), MEMF_CLEAR))
  111.     {
  112.     if(newsym->gs_Node.h_Name = savestring(name))
  113.     {
  114.         inserthash(SymbolTab, &newsym->gs_Node, GSYMTABSIZE);
  115.         newsym->gs_Sym.sym_Type = symType;
  116.         dupvalue(&newsym->gs_Sym.sym_Value, value);
  117.         return(newsym);
  118.     }
  119.     FreeVec(newsym);
  120.     }
  121.     settitle(NoMemMsg);
  122.     return(FALSE);
  123. }
  124.  
  125. /*
  126.  * value (any strings) will be allocated or whatever.
  127.  */
  128. LSYM *
  129. addlsym(STRPTR name, BYTE symType, VALUE *value)
  130. {
  131.     LSYM *newsym;
  132. /*
  133.  *  if(newsym = findlsym(name))
  134.  *  {
  135.  *    releasevalue(&newsym->ls_Sym.sym_Value);
  136.  *    newsym->ls_Sym.sym_Type = symType;
  137.  *    dupvalue(&newsym->ls_Sym.sym_Value, value);
  138.  *    return(newsym);
  139.  *  }
  140.  *  else
  141.  */
  142.     if(newsym = AllocVec(sizeof(LSYM), MEMF_CLEAR))
  143.     {
  144.     if(newsym->ls_Node.ln_Name = savestring(name))
  145.     {
  146.         newsym->ls_Sym.sym_Type = symType;
  147.         dupvalue(&newsym->ls_Sym.sym_Value, value);
  148.         AddTail(&CSList[CSDepth].cs_Locals, &newsym->ls_Node);
  149.         return(newsym);
  150.     }
  151.     FreeVec(newsym);
  152.     }
  153.     settitle(NoMemMsg);
  154.     return(FALSE);
  155. }
  156.  
  157. /*
  158.  * symbol's value is automatically discarded
  159.  */
  160. BOOL
  161. remsym(STRPTR name)
  162. {
  163.     BOOL rc = FALSE;
  164.     GSYM *gsym;
  165.     LSYM *lsym;
  166.     if(lsym = findlsym(name))
  167.     {
  168.     Remove(&lsym->ls_Node);
  169.     freestring(lsym->ls_Node.ln_Name);
  170.     releasevalue(&lsym->ls_Sym.sym_Value);
  171.     FreeVec(lsym);
  172.     rc = TRUE;
  173.     }
  174.     else if(gsym = findgsym(name))
  175.     {
  176.     removehash(SymbolTab, &gsym->gs_Node, GSYMTABSIZE);
  177.     releasevalue(&gsym->gs_Sym.sym_Value);
  178.     FreeVec(gsym);
  179.     rc = TRUE;
  180.     }
  181.     else
  182.     settitlefmt("error: unknown symbol %s", (LONG)name);
  183.     return(rc);
  184. }
  185.  
  186. /*
  187.  * Evaluate the next clause in the string (deals with all other clauses
  188.  * included in this clause).
  189.  *
  190.  * If this command returns FALSE it means a relatively major problem,
  191.  * normally syntax errors but it could be that the last window was closed
  192.  * in the middle of a command string, or that a clause was screwed up (ie,
  193.  * unknown symbol or something), or we ran out of stack.
  194.  */
  195. Local VALUE *
  196. resolveclause(STRPTR *clause, VALUE *result)
  197. {
  198.     STRPTR symbol = *clause;
  199.     UBYTE c;
  200.     if(stksize() <= MIN_STACK)
  201.     {
  202.     settitle("error: no stack available for deeper recursion");
  203.     goto error;
  204.     }
  205.     symbol = nextclause(symbol);
  206.     c = *symbol;
  207.     if((c == '(') || isalpha(c) || (c == '_'))
  208.     {
  209.     UBYTE name[40], d;
  210.     static const UBYTE dummy[] = ")";
  211.     STRPTR tname = name, dummy_p = dummy;
  212.     VALUE *argv;
  213.     LONG argc;
  214.     if(c == '(')
  215.         symbol = nextclause(symbol + 1);
  216.     while((d = *symbol) && (!isspace(d)) && (d != ')'))
  217.     {
  218.         *tname++ = d;
  219.         symbol++;
  220.     }
  221.     *tname = 0;
  222.     if(c == '(' ? buildargv(&symbol, &argc, &argv) : buildargv(&dummy_p, &argc, &argv))
  223.     {
  224.         result = resolvesym(name, argc, argv, result);
  225.         releaseargs(argc, argv);
  226.     }
  227.     }
  228.     else if((c == '`') || (c == '{'))
  229.     {
  230.     STRPTR tempbuff = AllocVec(1024, 0L);
  231.     LONG buffsize = 1024;
  232.  
  233.     if(tempbuff)
  234.     {
  235.         WORD kg = 0;
  236.         WORD quotecount = 1;
  237.         LONG i = 0;
  238.  
  239.         symbol++;
  240.         while(!kg)
  241.         {
  242.         UBYTE c = *symbol++;
  243.         switch(c)
  244.         {
  245.             case 0:
  246.             kg = -1;
  247.             symbol--;
  248.             settitle("syntax error: unbalanced quotes in string clause");
  249.             break;
  250.             case '`':
  251.             case '{':
  252.             quotecount++;
  253.             break;
  254.             case '\'':
  255.             case '}':
  256.             if(!(--quotecount))
  257.             {
  258.                 kg = 1;
  259.                 c = 0;
  260.             }
  261.             break;
  262.             case '\\':
  263.             if(quotecount == 1)
  264.                 c = escchar(&symbol);
  265.             else
  266.             {
  267.                 if(i >= buffsize)
  268.                 {
  269.                 STRPTR newbuff = AllocVec(buffsize << 1, 0L);
  270.                 if(!newbuff)
  271.                 {
  272.                     settitle(NoMemMsg);
  273.                     FreeVec(tempbuff);
  274.                     goto error;
  275.                 }
  276.                 memcpy(newbuff, tempbuff, buffsize);
  277.                 FreeVec(tempbuff);
  278.                 tempbuff = newbuff;
  279.                 buffsize <<= 1;
  280.                 }
  281.                 tempbuff[i++] = c;
  282.                 c = *symbol++;
  283.             }
  284.             break;
  285.         }
  286.         if(i >= buffsize)
  287.         {
  288.             STRPTR newbuff = AllocVec(buffsize << 1, 0L);
  289.             if(!newbuff)
  290.             {
  291.             settitle(NoMemMsg);
  292.             FreeVec(tempbuff);
  293.             goto error;
  294.             }
  295.             memcpy(newbuff, tempbuff, buffsize);
  296.             FreeVec(tempbuff);
  297.             tempbuff = newbuff;
  298.             buffsize <<= 1;
  299.         }
  300.         tempbuff[i++] = c;
  301.         }
  302.         if(kg == 1)
  303.         {
  304.         if(!(result->val_Value.String = savestring(tempbuff)))
  305.         {
  306.             settitle(NoMemMsg);
  307.             FreeVec(tempbuff);
  308.             goto error;
  309.         }
  310.         result->val_Type = VTF_STRING;
  311.         }
  312.         else
  313.         {
  314.         FreeVec(tempbuff);
  315.         goto error;
  316.         }
  317.         FreeVec(tempbuff);
  318.     }
  319.     else
  320.     {
  321.         settitle(NoMemMsg);
  322.         goto error;
  323.     }
  324.     }
  325.     else if((isdigit(c)) || (c == '-'))
  326.     {
  327.     result->val_Value.Number = strtol(symbol, &symbol, 0);
  328.     result->val_Type = VTF_NUMBER;
  329.     }
  330.     else if(c == '~')
  331.     {
  332.     if(symbol[1] == '\\')
  333.     {
  334.         symbol += 2;
  335.         result->val_Value.Number = (LONG)escchar(&symbol);
  336.     }
  337.     else
  338.     {
  339.         result->val_Value.Number = (LONG)symbol[1];
  340.         symbol += 2;
  341.     }
  342.     result->val_Type = VTF_NUMBER;
  343.     }
  344.     else if(c == '^')
  345.     {
  346.     result->val_Value.Number = (LONG)(toupper(symbol[1]) - '@');
  347.     result->val_Type = VTF_NUMBER;
  348.     symbol += 2;
  349.     }
  350.     else if(c == '@')
  351.     {
  352.     result->val_Type = VTF_NONE;
  353.     symbol++;
  354.     }
  355.     else
  356.     {
  357.     settitle("syntax error: unrecognized clause type");
  358. error:
  359.     /* In a perfect world I'd just step over this clause, but since
  360.      * I'm lazy that can wait and for now I'll skip the whole string.
  361.      */
  362.     symbol += strlen(symbol);
  363.     result->val_Type = VTF_NONE;
  364.     result = FALSE;
  365.     }
  366.     *clause = nextclause(symbol);
  367.     return(result);
  368. }
  369.  
  370. /*
  371.  * build up the argv array
  372.  *
  373.  * Format of the argv array is,
  374.  * argv[0]   - VALUE structure for result returned from command function,
  375.  * argv[1-n] - VALUE structure of each argument.
  376.  *
  377.  * The argc passed is the number of arguments, doesn't include the
  378.  * result.
  379.  * The number of arguments is only limited by memory availability.
  380.  */
  381. Local BOOL
  382. buildargv(STRPTR *args_p, LONG *argc_p, VALUE **argv_p)
  383. {
  384.     WORD maxargs = 9;
  385.     VALUE *argv = AllocVec(sizeof(VALUE) * maxargs, MEMF_CLEAR);
  386.     if(argv)
  387.     {
  388.     LONG i;
  389.     STRPTR args = *args_p;
  390.     for(i = 0; *args != ')'; i++)
  391.     {
  392.         if(i == (maxargs - 1))
  393.         {
  394.         VALUE *newargv = AllocVec(sizeof(VALUE) * (maxargs << 1), MEMF_CLEAR);
  395.         if(!newargv)
  396.         {
  397.             settitle(NoMemMsg);
  398.             releaseargs(i, argv);
  399.             return(FALSE);
  400.         }
  401.         memcpy(newargv, argv, sizeof(VALUE) * (i + 1));
  402.         FreeVec(argv);
  403.         argv = newargv;
  404.         maxargs <<= 1;
  405.         }
  406.         if(!(resolveclause(&args, &argv[i + 1])))
  407.         {
  408.         releaseargs(i, argv);
  409.         return(FALSE);
  410.         }
  411.     }
  412.     *args_p = args + 1;
  413.     *argv_p = argv;
  414.     *argc_p = i;
  415.     return(TRUE);
  416.     }
  417.     settitle(NoMemMsg);
  418.     return(FALSE);
  419. }
  420.  
  421. /*
  422.  * find where a symbol is and resolve it
  423.  */
  424. Local VALUE *
  425. resolvesym(STRPTR name, LONG argc, VALUE *argv, VALUE *result)
  426. {
  427.     LSYM *lsym;
  428.     GSYM *gsym;
  429.     UBYTE tmpbuf[256];
  430.  
  431.     /* Try local symbols first
  432.      */
  433.     if(lsym = findlsym(name))
  434.     result = execsym(&lsym->ls_Sym, argc, argv, result);
  435.  
  436.     /* Look for a global symbol
  437.      */
  438.     else if(gsym = findgsym(name))
  439.     result = execsym(&gsym->gs_Sym, argc, argv, result);
  440.  
  441.     /* See if we can find a DOS variable which fits
  442.      */
  443.     else if(GetVar(name, tmpbuf, 256, 0) > 0)
  444.     {
  445.     if(result->val_Value.String = savestring(tmpbuf))
  446.         result->val_Type = VTF_STRING;
  447.     else
  448.     {
  449.         settitle(NoMemMsg);
  450.         result->val_Type = VTF_NONE;
  451.         result = FALSE;
  452.     }
  453.     }
  454.  
  455.     /* Try for implicit REXX macro
  456.      */
  457.     else
  458.     {
  459.     if(strlen(name) < 200)
  460.     {
  461.         BPTR lock;
  462.         sprintf(tmpbuf, "REXX:%s.jed", name);
  463.         if(lock = Lock(tmpbuf, SHARED_LOCK))
  464.         {
  465.         UnLock(lock);
  466.         strcpy(tmpbuf, name);
  467.         if(expandargs(tmpbuf, argc, argv, 256))
  468.         {
  469.             if(!(result->val_Value.Number = asyncRexxCmd(tmpbuf)))
  470.             goto nosym;
  471.             result->val_Type = VTF_NUMBER;
  472.         }
  473.         else
  474.         {
  475.             settitle("error: buffer overflow");
  476.             result = FALSE;
  477.         }
  478.         }
  479.         else
  480.         goto nosym;
  481.     }
  482.     else
  483.     {
  484.         settitle(NoMemMsg);
  485.         result = FALSE;
  486.     }
  487.     }
  488.     if(FALSE)
  489.     {
  490. nosym:
  491.     settitlefmt("error: can't find symbol %s", (LONG)name);
  492.     result = FALSE;
  493.     }
  494.     return(result);
  495. }
  496.  
  497. /*
  498.  * actually executes (or whatever) a symbol
  499.  */
  500. Local VALUE *
  501. execsym(SYM *sym, LONG argc, VALUE *argv, VALUE *result)
  502. {
  503.     switch(sym->sym_Value.val_Type)
  504.     {
  505.     case VTF_FUNC:
  506.         if(!sym->sym_Value.val_Value.Func(argc, argv))
  507.         {
  508.         *result = RES;
  509.         result = NULL;
  510.         }
  511.         else
  512.         *result = RES;
  513.         refresh();
  514.         break;
  515.     case VTF_STRING:
  516.         if(sym->sym_Type == STF_COMMAND)
  517.         result = execstr(sym->sym_Value.val_Value.String, result, TRUE, argc, argv);
  518.         else
  519.         dupvalue(result, &sym->sym_Value);
  520.         break;
  521.     default:
  522.         dupvalue(result, &sym->sym_Value);
  523.         break;
  524.     }
  525.     return(result);
  526. }
  527.  
  528. /*
  529.  * expands a commands argument clauses into a string (for REXX)
  530.  */
  531. Local BOOL
  532. expandargs(STRPTR dest, LONG argc, VALUE *argv, LONG maxd)
  533. {
  534.     LONG i = strlen(dest);
  535.     while(argc--)
  536.     {
  537.     switch(argv->val_Type)
  538.     {
  539.         case VTF_STRING:
  540.         LONG strl = strlen(argv->val_Value.String);
  541.         if(i + strl < maxd)
  542.         {
  543.             strcpy(dest + i, " ");
  544.             strcpy(dest + i + 1, argv->val_Value.String);
  545.             i += strl + 1;
  546.         }
  547.         else
  548.         {
  549.             settitle("error: internal buffer overflow");
  550.             return(FALSE);
  551.         }
  552.         break;
  553.         case VTF_NUMBER:
  554.         UBYTE nbuf[13];
  555.         LONG strl;
  556.         sprintf(nbuf, "%ld", argv->val_Value.Number);
  557.         strl = strlen(nbuf);
  558.         if(i + strl < maxd)
  559.         {
  560.             strcpy(dest + i, " ");
  561.             strcpy(dest + i + 1, nbuf);
  562.             i += strl + 1;
  563.         }
  564.         else
  565.             return(FALSE);
  566.         break;
  567.     }
  568.     argv++;
  569.     }
  570.     return(TRUE);
  571. }
  572.  
  573. VOID
  574. releasevalue(VALUE *value)
  575. {
  576.     if(value->val_Type == VTF_STRING)
  577.     freestring(value->val_Value.String);
  578.     value->val_Type = VTF_NONE;
  579. }
  580.  
  581. BOOL
  582. dupvalue(VALUE *dst, VALUE *src)
  583. {
  584.     BOOL rc = TRUE;
  585.     if(src->val_Type == VTF_STRING)
  586.     {
  587.     if(dst->val_Value.String = savestring(src->val_Value.String))
  588.         dst->val_Type = VTF_STRING;
  589.     else
  590.     {
  591.         dst->val_Type = VTF_NONE;
  592.         rc = FALSE;
  593.     }
  594.     }
  595.     else
  596.     *dst = *src;
  597.     return(rc);
  598. }
  599.  
  600. /*
  601.  * Doesn't touch the result field
  602.  */
  603. Local VOID
  604. releaseargs(LONG argc, VALUE *argv)
  605. {
  606.     if(argv)
  607.     {
  608.     LONG i;
  609.     for(i = 1; i <= argc; i++)
  610.     {
  611.         if(argv[i].val_Type == VTF_STRING)
  612.         freestring(argv[i].val_Value.String);
  613.     }
  614.     FreeVec(argv);
  615.     }
  616. }
  617.  
  618. /*
  619.  * checks the arguments passed to a command against the provided
  620.  * template. template has two bits per argument (ARG1 is LSB)
  621.  * these bits represent,
  622.  * 00    anything
  623.  * 01    string
  624.  * 10    number
  625.  * macros are provided for this function, ie
  626.  * if(TPLATE2(VTF_NUMBER, VTF_STRING))
  627.  */
  628. BOOL
  629. templatecmd(LONG argc, VALUE *argv, LONG argTemplate)
  630. {
  631.     LONG i;
  632.     for (i = 1; i <= argc; i++)
  633.     {
  634.     LONG thismask = argTemplate & 3;
  635.     switch(thismask)
  636.     {
  637.         case 1:
  638.         if(ARG(i).val_Type != VTF_STRING)
  639.         {
  640.             settitlefmt("syntax error: argument %ld should be a string", i);
  641.             return(FALSE);
  642.         }
  643.         break;
  644.         case 2:
  645.         if(ARG(i).val_Type != VTF_NUMBER)
  646.         {
  647.             settitlefmt("syntax error: argument %ld should be a number", i);
  648.             return(FALSE);
  649.         }
  650.         break;
  651.     }
  652.     argTemplate >>= 2;
  653.     }
  654.     if(argTemplate)
  655.     {
  656.     settitle("syntax error: too few arguments");
  657.     return(FALSE);
  658.     }
  659.     return(TRUE);
  660. }
  661.  
  662. /*
  663.  * Executes a string of clauses, arguments can be passed and it can return
  664.  * a result.
  665.  *
  666.  * A command in the macro which returns a null VALUE only aborts a macro
  667.  * string (defined as those having arguments) - the result of the macro is
  668.  * the result of the command that returned NULL.
  669.  *
  670.  * internal:
  671.  * If this command returns 0 then it means that the result (in the VALUE
  672.  * passed in) should be propagated back through the strings being executed
  673.  * up to the macro (if available).
  674.  * If a command returns 0 and result type is VTF_BREAK we just break from
  675.  * this string returning a VTF_NONE value. Actually VTF_BREAK can handle
  676.  * multiple depth breaking.
  677.  */
  678. VALUE *
  679. execstr(STRPTR string, VALUE *result, BOOL args, LONG argc, VALUE *argv)
  680. {
  681.     if(++CSDepth < MAX_CS_RECURSE)
  682.     {
  683.     LSYM *thissym, *nextsym;
  684.     CS *cs = &CSList[CSDepth];
  685.     cs->cs_Args = args;
  686.     cs->cs_Argc = argc;
  687.     cs->cs_Argv = argv;
  688.     NewList(&cs->cs_Locals);
  689.     result->val_Type = VTF_NONE;
  690.     while(*string)
  691.     {
  692.         releasevalue(result);
  693.         if(!resolveclause(&string, result))
  694.         {
  695.         if(result->val_Type == VTF_BREAK)
  696.         {
  697.             if(!(--result->val_Value.Number))
  698.             result->val_Type = VTF_NONE;
  699.             else
  700.             result = FALSE;
  701.         }
  702.         else if(!args)
  703.             result = FALSE;
  704.         break;
  705.         }
  706.     }
  707.     for(thissym = (LSYM *)cs->cs_Locals.lh_Head; thissym->ls_Node.ln_Succ; thissym = nextsym)
  708.     {
  709.         nextsym = (LSYM *)thissym->ls_Node.ln_Succ;
  710.         freestring(thissym->ls_Node.ln_Name);
  711.         releasevalue(&thissym->ls_Sym.sym_Value);
  712.         FreeVec(thissym);
  713.     }
  714.     }
  715.     else
  716.     {
  717.     settitle("error: command string recursion too deep");
  718.     result->val_Type = VTF_NONE;
  719.     }
  720.     CSDepth--;
  721.     return(result);
  722. }
  723.  
  724. LSYM *
  725. findlsym(STRPTR name)
  726. {
  727.     WORD i;
  728.     for(i = CSDepth; i; i--)
  729.     {
  730.     LSYM *this;
  731.     for(this = (LSYM *)CSList[i].cs_Locals.lh_Head; this->ls_Node.ln_Succ; this = (LSYM *)this->ls_Node.ln_Succ)
  732.     {
  733.         if(!strcmp(name, this->ls_Node.ln_Name))
  734.         return(this);
  735.     }
  736.     }
  737.     return(FALSE);
  738. }
  739.  
  740. /*
  741.  * find the arguments given to the innermost macro
  742.  */
  743. BOOL
  744. findsargs(LONG *argc_p, VALUE **argv_p)
  745. {
  746.     WORD i;
  747.     for(i = CSDepth; i; i--)
  748.     {
  749.     if(CSList[i].cs_Args)
  750.     {
  751.         *argc_p = CSList[i].cs_Argc;
  752.         *argv_p = CSList[i].cs_Argv;
  753.         return(TRUE);
  754.     }
  755.     }
  756.     return(FALSE);
  757. }
  758.  
  759. VOID
  760. rxexecstring(STRPTR string, APTR rexxMsg)
  761. {
  762.     VALUE result;
  763.     cursor(OFF);
  764.     if(execstr(string, &result, FALSE, 0, NULL))
  765.     {
  766.     if(result.val_Type == VTF_STRING)
  767.         replyRexxCmd(rexxMsg, 0, 0, result.val_Value.String);
  768.     else if(result.val_Type == VTF_NUMBER)
  769.         replyRexxCmd(rexxMsg, result.val_Value.Number, 0, NULL);
  770.     else
  771.         replyRexxCmd(rexxMsg, 10, 0, NULL);
  772.     }
  773.     else
  774.     replyRexxCmd(rexxMsg, 10, 0, NULL);
  775.     releasevalue(&result);
  776.     cursor(ON);
  777. }
  778.  
  779. /*
  780.  * Used by _main() to execute startup script
  781.  */
  782. BOOL
  783. scriptfile(STRPTR fileName)
  784. {
  785.     STRPTR filetext;
  786.     if(filetext = squirrelfile(fileName))
  787.     {
  788.     VALUE result;
  789.     settitlefmt("executing %s...", (LONG)fileName);
  790.     cursor(OFF);
  791.     execstr(filetext, &result, FALSE, 0, NULL);
  792.     releasevalue(&result);
  793.     cursor(ON);
  794.     freestring(filetext);
  795.     return(TRUE);
  796.     }
  797.     return(FALSE);
  798. }
  799.  
  800. /*
  801.  * Steps over white space, if a character COMMENT_CHAR (';') is found
  802.  * the rest of the line is skipped.
  803.  *
  804.  * IMPORTANT:
  805.  *   If a command string is given to a command by enclosing it in quotes
  806.  * it may contain comments (which won't be stripped until the command is
  807.  * executed) but they (the comments) must NOT contain un-escaped quotes.
  808.  */
  809. Local STRPTR
  810. nextclause(STRPTR str)
  811. {
  812.     STRPTR str2 = str;
  813.     for(;;)
  814.     {
  815.     UBYTE c;
  816.     while(isspace(c = *str2++))
  817.         ;
  818.  
  819.     if(c == COMMENT_CHAR)
  820.     {
  821.         while((c = *str2++) && (c != '\n'))
  822.         ;
  823.         if(!c)
  824.         str2--;
  825.     }
  826.     else
  827.         return(str2 - 1);
  828.     }
  829. }
  830.  
  831. /*
  832.  * (symboldump `file' `type')
  833.  *
  834.  * type =
  835.  *        globals
  836.  *        locals
  837.  *        all
  838.  */
  839. VALUE *
  840. cmd_symboldump(LONG argc, VALUE *argv)
  841. {
  842.     if(TPLATE2(VTF_STRING, VTF_STRING))
  843.     {
  844.     BPTR fh = Open(ARG1.val_Value.String, MODE_NEWFILE);
  845.     if(fh)
  846.     {
  847.         BOOL globs = FALSE;
  848.         BOOL locs = FALSE;
  849.         FPuts(fh, "symbol dump\n");
  850.         if(!stricmp("globals", ARG2.val_Value.String))
  851.         globs = TRUE;
  852.         else if(!stricmp("locals", ARG2.val_Value.String))
  853.         locs = TRUE;
  854.         else if(!stricmp("all", ARG2.val_Value.String))
  855.         {
  856.         globs = TRUE;
  857.         locs = TRUE;
  858.         }
  859.         if(globs)
  860.         {
  861.         WORD i;
  862.         FPuts(fh, "\nglobal symboltable\n");
  863.         for(i = 0; i < GSYMTABSIZE; i++)
  864.         {
  865.             GSYM *thissym = SymbolTab[i];
  866.             while(thissym)
  867.             {
  868.             FPrintf(fh, "\t%s", (LONG)thissym->gs_Node.h_Name);
  869.             dumponesym(&thissym->gs_Sym, fh);
  870.             thissym = thissym->gs_Node.h_Next;
  871.             }
  872.         }
  873.         }
  874.         if(locs)
  875.         {
  876.         WORD i;
  877.         FPuts(fh, "\nlocal symbols\n");
  878.         for(i = 1; i <= CSDepth; i++)
  879.         {
  880.             LSYM *thissym = (LSYM *)CSList[i].cs_Locals.lh_Head;
  881.             FPrintf(fh, "\tdepth %ld\n", i);
  882.             while(thissym->ls_Node.ln_Succ)
  883.             {
  884.             FPrintf(fh, "\t\t%s", (LONG)thissym->ls_Node.ln_Name);
  885.             dumponesym(&thissym->ls_Sym, fh);
  886.             thissym = thissym->ls_Node.ln_Succ;
  887.             }
  888.         }
  889.         }
  890.         Close(fh);
  891.         setnumres(TRUE);
  892.     }
  893.     else
  894.         setnumres(FALSE);
  895.     }
  896.     return(&RES);
  897. }
  898.  
  899. Local VOID
  900. dumponesym(SYM *sym, BPTR fh)
  901. {
  902.     if(sym->sym_Type == STF_COMMAND)
  903.     FPuts(fh, " (command)");
  904.     else if(sym->sym_Type == STF_VARIABLE)
  905.     FPuts(fh, " (variable)");
  906.     else
  907.     FPuts(fh, " (unknown symbol type)");
  908.  
  909.     if(sym->sym_Value.val_Type == VTF_NONE)
  910.     FPuts(fh, " = no value\n");
  911.     else if(sym->sym_Value.val_Type == VTF_STRING)
  912.     FPrintf(fh, " = {%s}\n", sym->sym_Value.val_Value.Number);
  913.     else if(sym->sym_Value.val_Type == VTF_NUMBER)
  914.     FPrintf(fh, " = 0x%lx\n", sym->sym_Value.val_Value.Number);
  915.     else if(sym->sym_Value.val_Type == VTF_FUNC)
  916.     FPuts(fh, " = primitive function\n");
  917.     else
  918.     FPuts(fh, " unknown value type\n");
  919. }
  920.